View Javadoc
1   package edu.jiangxin.apktoolbox.file.password.recovery.category.dictionary.multithread;
2   
3   import edu.jiangxin.apktoolbox.file.core.EncoderDetector;
4   import edu.jiangxin.apktoolbox.file.password.recovery.RecoveryPanel;
5   import edu.jiangxin.apktoolbox.file.password.recovery.State;
6   import org.apache.logging.log4j.LogManager;
7   import org.apache.logging.log4j.Logger;
8   
9   import java.io.*;
10  import java.nio.MappedByteBuffer;
11  import java.nio.channels.FileChannel;
12  import java.util.HashSet;
13  import java.util.Set;
14  import java.util.concurrent.BrokenBarrierException;
15  import java.util.concurrent.CyclicBarrier;
16  import java.util.concurrent.ScheduledThreadPoolExecutor;
17  import java.util.concurrent.atomic.AtomicBoolean;
18  import java.util.concurrent.atomic.AtomicInteger;
19  
20  public class BigFileReader {
21      private static final Logger logger = LogManager.getLogger(BigFileReader.class.getSimpleName());
22  
23      private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;
24  
25      private static final int PROCESSOR_COUNT = Runtime.getRuntime().availableProcessors();
26  
27      private final String charset;
28      private final int bufferSize;
29      private final ScheduledThreadPoolExecutor executorService;
30      private final long fileLength;
31      private RandomAccessFile rAccessFile;
32      private final Set<StartEndPair> startEndPairs;
33      private CyclicBarrier cyclicBarrier;
34      private final AtomicInteger counter = new AtomicInteger(0);
35      private final CompleteCallback callback;
36  
37      private final AtomicBoolean success = new AtomicBoolean(false);
38  
39      private final RecoveryPanel panel;
40  
41      public BigFileReader(CompleteCallback callback, RecoveryPanel panel) {
42          this.panel = panel;
43          File file = panel.getDictionaryFile();
44          if (!file.exists()) {
45              throw new IllegalArgumentException("文件不存在!");
46          }
47          this.fileLength = file.length();
48          this.charset = EncoderDetector.judgeFile(file.getAbsolutePath());
49          this.bufferSize = DEFAULT_BUFFER_SIZE;
50          try {
51              this.rAccessFile = new RandomAccessFile(file, "r");
52          } catch (FileNotFoundException e) {
53              logger.error("BigFileReader FileNotFoundException");
54          }
55          this.executorService = new ScheduledThreadPoolExecutor(PROCESSOR_COUNT);
56          this.startEndPairs = new HashSet<>();
57          this.callback = callback;
58      }
59  
60      public void start() {
61          long everySize = fileLength / PROCESSOR_COUNT;
62          try {
63              calculateStartEnd(0, everySize);
64          } catch (IOException e) {
65              logger.error("start", e);
66              return;
67          }
68  
69          final long startTime = System.currentTimeMillis();
70          int parties = startEndPairs.size();
71          logger.info("[TaskTracing]Parties: " + parties);
72          cyclicBarrier = new CyclicBarrier(parties, () -> {
73              logger.info("use time: " + (System.currentTimeMillis() - startTime) + "ms");
74              logger.info("all line: " + counter.get());
75              callback.onComplete(null);
76          });
77          for (StartEndPair pair : startEndPairs) {
78              logger.info("pair: " + pair);
79              executorService.execute(new SliceReaderTask(pair));
80          }
81      }
82  
83      private void calculateStartEnd(long start, long size) throws IOException {
84          if (start > fileLength - 1) {
85              return;
86          }
87          StartEndPair pair = new StartEndPair();
88          pair.start = start;
89          long endPosition = start + size - 1;
90          if (endPosition >= fileLength - 1) {
91              pair.end = fileLength - 1;
92              startEndPairs.add(pair);
93              return;
94          }
95  
96          rAccessFile.seek(endPosition);
97          byte tmp = (byte) rAccessFile.read();
98          while (tmp != '\n' && tmp != '\r') {
99              endPosition++;
100             if (endPosition >= fileLength - 1) {
101                 endPosition = fileLength - 1;
102                 break;
103             }
104             rAccessFile.seek(endPosition);
105             tmp = (byte) rAccessFile.read();
106         }
107         pair.end = endPosition;
108         startEndPairs.add(pair);
109 
110         calculateStartEnd(endPosition + 1, size);
111     }
112 
113     public void shutdown() {
114         try {
115             rAccessFile.close();
116         } catch (IOException e) {
117             logger.error("shutdown IOException");
118         }
119         executorService.shutdown();
120         logger.info("shutdown executorService");
121     }
122 
123     private void handle(byte[] bytes) throws UnsupportedEncodingException {
124         if (success.compareAndSet(true, true) || panel.getCurrentState() != State.WORKING) {
125             return;
126         }
127 
128         String line;
129         if (charset == null) {
130             line = new String(bytes);
131         } else {
132             line = new String(bytes, charset);
133         }
134 
135         panel.setCurrentPassword(line);
136         panel.increaseProgressBarValue();
137         counter.decrementAndGet();
138 
139         if (panel.getCurrentFileChecker().checkPassword(line)) {
140             if (success.compareAndSet(false, true)) {
141                 logger.info("find password: {}", line);
142                 callback.onComplete(line);
143             }
144         } else {
145             if (!success.compareAndSet(true, true) && panel.getCurrentState() == State.WORKING) {
146                 logger.info("try password[{}] failed", line);
147             }
148         }
149     }
150 
151     private static class StartEndPair {
152         public long start;
153         public long end;
154 
155         @Override
156         public String toString() {
157             return "star=" + start + ";end=" + end;
158         }
159 
160         @Override
161         public int hashCode() {
162             final int prime = 31;
163             int result = 1;
164             result = prime * result + (int) (end ^ (end >>> 32));
165             result = prime * result + (int) (start ^ (start >>> 32));
166             return result;
167         }
168 
169         @Override
170         public boolean equals(Object obj) {
171             if (this == obj)
172                 return true;
173             if (obj == null)
174                 return false;
175             if (getClass() != obj.getClass())
176                 return false;
177             StartEndPair other = (StartEndPair) obj;
178             if (end != other.end)
179                 return false;
180             return start == other.start;
181         }
182 
183     }
184 
185     private class SliceReaderTask implements Runnable {
186         private final long start;
187         private final long sliceSize;
188         private final byte[] readBuff;
189 
190         public SliceReaderTask(StartEndPair pair) {
191             this.start = pair.start;
192             this.sliceSize = pair.end - pair.start + 1;
193             this.readBuff = new byte[bufferSize];
194         }
195 
196         @Override
197         public void run() {
198             try {
199                 MappedByteBuffer mapBuffer = rAccessFile.getChannel().map(FileChannel.MapMode.READ_ONLY, start, this.sliceSize);
200                 ByteArrayOutputStream bos = new ByteArrayOutputStream();
201                 for (int offset = 0; offset < sliceSize; offset += bufferSize) {
202                     int readLength;
203                     if (offset + bufferSize <= sliceSize) {
204                         readLength = bufferSize;
205                     } else {
206                         readLength = (int) (sliceSize - offset);
207                     }
208                     mapBuffer.get(readBuff, 0, readLength);
209                     for (int i = 0; i < readLength; i++) {
210                         byte tmp = readBuff[i];
211                         if (tmp == '\n' || tmp == '\r') {
212                             if (bos.size() > 0) {
213                                 handle(bos.toByteArray());
214                             }
215                             bos.reset();
216                         } else {
217                             bos.write(tmp);
218                         }
219                     }
220                 }
221                 if (bos.size() > 0) {
222                     handle(bos.toByteArray());
223                 }
224                 logger.info("[TaskTracing]Waiting number: " + cyclicBarrier.getNumberWaiting());
225             } catch (Exception e) {
226                 logger.error("run Exception" + e.getMessage());
227             }
228             try {
229                 cyclicBarrier.await();
230             } catch (InterruptedException e) {
231                 logger.error("await InterruptedException");
232                 Thread.currentThread().interrupt();
233             } catch (BrokenBarrierException e) {
234                 logger.error("await BrokenBarrierException");
235             }
236         }
237 
238     }
239 }